import itertools
from struct import unpack

class parseSTL():
    def __init__(self):
        self.file = ""
        self.T = []
        self.min = 0.0
        self.max = 0.0
        
        self.commands=["solid","facet","normal","outer", "loop","vertex","endloop","endfacet","endsolid"]
        self.name = ""
        self.offset = 0
        self.voffset = 0
    
    def setFile(self,filename):
        self.file=filename
    
    def getTriangles(self):
        return self.T
    
    def getName(self):
        return self.name
    
    def getMin(self):
        return self.min
    
    def getMax(self):
        return self.max
    
    def parseFile(self):
        self.offset = 0
        self.T = []
        self.name = ""
        first = True
        f = open(self.file,'rb')
        for line in f:
            cmds = line.strip().split(' ')
            cmd = cmds.pop(0)
            if (cmd not in self.commands):
                #Assume binary STL file if first command is not "solid"
                if first:
                    f.seek(0,0)
                    self.parseBin(f)
                    break
                #WTF?!
                print 'Encountered unexpected command!'
                print cmd
                return False
            getattr(self, cmd)((' '.join(cmds)))
            first = False
        f.close()
        return True
        
    def read(self, f, length):
        tmp = length
        buf = ""
        while tmp > 0:
            tmp2 = f.read(tmp)
            if tmp2 == "":
                break
            tmp -= len(tmp2)
            buf += tmp2
        return buf

    def parseBin(self, f):
        self.name = self.read(f, 80)
        length = int(unpack("=i", self.read(f, 4))[0])
        self.T = [[[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]]*length
        while self.offset < length:
            self.ntmp = [0.0,0.0,0.0]
            self.ntmp[0] = float(unpack("=f", self.read(f, 4))[0])
            self.ntmp[1] = float(unpack("=f", self.read(f, 4))[0])
            self.ntmp[2] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][0][0] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][0][1] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][0][2] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][1][0] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][1][1] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][1][2] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][2][0] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][2][1] = float(unpack("=f", self.read(f, 4))[0])
            self.T[self.offset][2][2] = float(unpack("=f", self.read(f, 4))[0])
            #ignore attribute bytes
            self.read(f,2)
            self.T[self.offset] = sorted(self.T[self.offset],key=lambda t:t[2])
            if (self.T[self.offset][0][2] < self.min):
                self.min = self.T[self.offset][0][2]
            if (self.T[self.offset][2][2] > self.max):
                self.max = self.T[self.offset][2][2]
            self.T[self.offset].append(self.ntmp)
            self.offset += 1
            
    def solid(self, line):
        self.name = line
        
    def facet(self, line):
        self.T.append([[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]])
        self.ntmp = [0.0,0.0,0.0]
        cmds = line.strip().split(' ')
        cmd = cmds.pop(0)
        getattr(self, cmd)(' '.join(cmds))
    
    def normal(self, line):
        tmp = line.strip().split(' ')
        self.ntmp[0] = float(tmp[0])
        self.ntmp[1] = float(tmp[1])
        self.ntmp[2] = float(tmp[2])
        
    def outer(self, line):
        getattr(self,line)()
    
    def loop(self):
        self.voffset = 0
        return
    
    def vertex(self,line):
        tmp = line.strip().split(' ')
        self.T[self.offset][self.voffset][0] = float(tmp[0])
        self.T[self.offset][self.voffset][1] = float(tmp[1])
        self.T[self.offset][self.voffset][2] = float(tmp[2])
        self.voffset += 1
        
    def endloop(self,line):
        return
    
    def endfacet(self,line):
        self.T[self.offset] = sorted(self.T[self.offset],key=lambda t:t[2])
        
        if (self.T[self.offset][0][2] < self.min):
            self.min = self.T[self.offset][0][2]
        if (self.T[self.offset][2][2] > self.max):
            self.max = self.T[self.offset][2][2]
            
        self.T[self.offset].append(self.ntmp)
        self.offset += 1
        
    def endsolid(self,line):
        return
        
        
        